
#include <Raknet/RakPeerInterface.h>
#include "wormnet/wm_RaknetChannel.h"

namespace worm
{

worm::wmRaknetChannel::wmRaknetChannel(wmConnectedCB pConnectedCB, wmReceiveCB pReceiveCB, wmDisconnectCB pDisconnectCB)
   : wmChannel(pConnectedCB, pReceiveCB, pDisconnectCB)
{
    uint8 i = 0;
    for (; i<ID_USER_PACKET_ENUM; i++)
    {
        m_pfnHandleMsg[i] = &wmRaknetChannel::None;
    }

    m_pfnHandleMsg[ID_DISCONNECTION_NOTIFICATION] = &wmRaknetChannel::Disconnection;
    m_pfnHandleMsg[ID_CONNECTION_LOST] = &wmRaknetChannel::Disconnection;
    m_pfnHandleMsg[ID_SND_RECEIPT_LOSS] = &wmRaknetChannel::Disconnection;
    m_pfnHandleMsg[ID_CONNECTION_REQUEST_ACCEPTED] = &wmRaknetChannel::ConnectionAccepted;
    m_pfnHandleMsg[ID_CONNECTION_ATTEMPT_FAILED] = &wmRaknetChannel::Disconnection;
    m_pfnHandleMsg[ID_INVALID_PASSWORD] = &wmRaknetChannel::Disconnection;
    m_pfnHandleMsg[ID_ALREADY_CONNECTED] = &wmRaknetChannel::Disconnection;
    m_pfnHandleMsg[ID_NO_FREE_INCOMING_CONNECTIONS] = &wmRaknetChannel::Disconnection;
    m_pfnHandleMsg[ID_WORM_RD] = &wmRaknetChannel::RecvPacket;

    m_pClient = NULL;
    m_pChannelAddr = NULL;
}

wmRaknetChannel::~wmRaknetChannel()
{
    delete ((RakNet::SystemAddress*)m_pChannelAddr);
}

bool wmRaknetChannel::ConnectServer(const char* szIp, const uint16& nPort)
{
    m_pClient = RakNet::RakPeerInterface::GetInstance();
    ((RakNet::RakPeerInterface*)m_pClient)->AllowConnectionResponseIPMigration(false);
    RakNet::SocketDescriptor socketDescriptor;
    socketDescriptor.port = 0;

    if (((RakNet::RakPeerInterface*)m_pClient)->Startup(1, &socketDescriptor, 1) != RakNet::RAKNET_STARTED)
    {
        return false;
    }
    ((RakNet::RakPeerInterface*)m_pClient)->SetOccasionalPing(true);

    if (((RakNet::RakPeerInterface*)m_pClient)->Connect(szIp, nPort, NULL, 0, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
    {
        return false;
    }

    return true;
}

void wmRaknetChannel::EventLoop()
{
    static RakNet::Packet* pPacket = NULL;
    pPacket = ((RakNet::RakPeerInterface*)m_pClient)->Receive();

    if (NULL == pPacket)
    {
        return;
    }
    if (0 == pPacket->length)
    {
        ((RakNet::RakPeerInterface*)m_pClient)->DeallocatePacket(pPacket);
        return;
    }
    if (pPacket->data[0] < ID_USER_PACKET_ENUM)
    {
        (this->*(m_pfnHandleMsg)[pPacket->data[0]])(pPacket);
    }
    ((RakNet::RakPeerInterface*)m_pClient)->DeallocatePacket(pPacket);
}

void wmRaknetChannel::Send(CByteBuffer& oPacket)
{
    if (NULL == m_pClient)
    {
        return;
    }

    ((RakNet::RakPeerInterface*)m_pClient)->Send(oPacket.data(), oPacket.size(), \
        IMMEDIATE_PRIORITY, RELIABLE_ORDERED, 0, *((RakNet::SystemAddress*)m_pChannelAddr), false);
}

void wmRaknetChannel::CloseConnection()
{
    ((RakNet::RakPeerInterface*)m_pClient)->Shutdown(25);
    m_pClient = NULL;
}

void wmRaknetChannel::None(void* pPacket)
{

}

void wmRaknetChannel::ConnectionAccepted(void* pPacket)
{
    m_pChannelAddr = new RakNet::SystemAddress();
    *((RakNet::SystemAddress*)m_pChannelAddr) = ((RakNet::Packet*)pPacket)->systemAddress;
    m_pConnectedCB(true);
}

void wmRaknetChannel::Disconnection(void* pPacket)
{
    m_pDisconnectCB();
}

void wmRaknetChannel::RecvPacket(void* pPacket)
{
    m_pReceiveCB((char*)((RakNet::Packet*)pPacket)->data, ((RakNet::Packet*)pPacket)->length);
}

}
